﻿using FrameWork.Language;
using FrameWork.Npoi.Helper;
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace FrameWork.Npoi
{
    /// <summary>
    /// 
    /// </summary>
    public class ExcelImportHelper
    {
        /// <summary>
        /// 读取excel生成实体
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="path"></param>
        /// <returns></returns>
        public static List<T> Load<T>(string path)
            where T : ImportModelBase, new()
        {
            var list = new List<T>();
            IWorkbook workbook = null;
            ISheet sheet = null;
            int rowCount = 0;
            int colCount = 0;
            IRow headers = null;
            FileStream fs = null;
            Dictionary<int, string> colIndex = new Dictionary<int, string>();

            PropertyInfo[] props = typeof(T).GetProperties();

            using (fs = File.OpenRead(path))
            {
                if (path.EndsWith(".xlsx"))
                    workbook = new XSSFWorkbook(fs);
                else if (path.EndsWith(".xls"))
                    workbook = new HSSFWorkbook(fs);

                sheet = workbook.GetSheetAt(0);
                rowCount = sheet.LastRowNum + 1;
                headers = sheet.GetRow(0);
                colCount = headers.LastCellNum;

                //列和列所在行对应缓存起来
                for (int c = 0; c < colCount; c++)
                {
                    var cell = headers.GetCell(c);
                    if (string.IsNullOrWhiteSpace(cell.StringCellValue))
                        throw new Exception("列名不能为空");
                    colIndex[c] = cell.StringCellValue.TrimEnd('*');
                }

                //从excel表第二行开始读取数据
                for (int r = 1; r < rowCount; r++)
                {
                    var row = sheet.GetRow(r);
                    if (row == null) continue;
                    var model = new T();
                    list.Add(model);
                    model.ImportStatus = enumExcelImportStatus.Ready;
                    try
                    {
                        for (int c = 0; c < colCount; c++)
                        {
                            var prop = props.FirstOrDefault(p =>
                            {
                                var des = p.GetCustomAttribute<DescriptionAttribute>();
                                if (des != null)
                                    return I18n.Get(des.Description) == colIndex[c];

                                return p.Name == colIndex[c];
                            });
                            if (prop == null) continue;

                            var cell = row.GetCell(c);
                            if (cell == null)
                            {
                                if (prop.GetCustomAttribute<RequiredAttribute>() != null)
                                {
                                    //必填项没有值 这一行设置错误状态 不再加载
                                    model.ImportStatus = enumExcelImportStatus.DataError;
                                    break;
                                }
                                continue;
                            }


                            if (cell.CellType == CellType.Numeric)
                            {
                                if (prop.PropertyType == typeof(DateTime) || prop.PropertyType == typeof(DateTime?))
                                {

                                    try
                                    {
                                        prop.SetValue(model, cell.DateCellValue);
                                    }
                                    catch
                                    {
                                        var days = cell.NumericCellValue;
                                        prop.SetValue(model, new DateTime(1900, 1, 1).AddDays(days));
                                    }
                                }
                                else if (prop.PropertyType == typeof(int) || prop.PropertyType == typeof(int?))
                                    prop.SetValue(model, (int)cell.NumericCellValue);
                                else if (prop.PropertyType == typeof(double) || prop.PropertyType == typeof(double?))
                                    prop.SetValue(model, cell.NumericCellValue);
                                else if (prop.PropertyType == typeof(decimal) || prop.PropertyType == typeof(decimal?))
                                    prop.SetValue(model, (decimal)cell.NumericCellValue);
                                else if (prop.PropertyType == typeof(float) || prop.PropertyType == typeof(float?))
                                    prop.SetValue(model, (float)cell.NumericCellValue);
                                else if (prop.PropertyType == typeof(string))
                                    prop.SetValue(model, cell.NumericCellValue.ToString());
                                else
                                {
                                    model.ImportStatus = enumExcelImportStatus.DataError;
                                    break;
                                }
                            }
                            else if (cell.CellType == CellType.Boolean)
                            {
                                if (prop.PropertyType == typeof(bool) || prop.PropertyType == typeof(bool?))
                                    prop.SetValue(model, cell.BooleanCellValue);
                                else if (prop.PropertyType == typeof(string))
                                    prop.SetValue(model, cell.BooleanCellValue.ToString());
                                else
                                {
                                    model.ImportStatus = enumExcelImportStatus.DataError;
                                    break;
                                }
                            }
                            else if (cell.CellType == CellType.String)
                            {
                                var str = cell.StringCellValue;

                                if (string.IsNullOrWhiteSpace(str) && prop.GetCustomAttribute<RequiredAttribute>() != null)
                                {
                                    //必填项没有值 这一行设置错误状态 不再加载
                                    model.ImportStatus = enumExcelImportStatus.DataError;
                                    break;
                                }

                                if (prop.PropertyType == typeof(string))
                                    prop.SetValue(model, str);
                                else if ((prop.PropertyType == typeof(DateTime) || prop.PropertyType == typeof(DateTime?)) && DateTime.TryParse(str, out DateTime dt))
                                    prop.SetValue(model, dt);
                                else if ((prop.PropertyType == typeof(int) || prop.PropertyType == typeof(int?)) && int.TryParse(str, out int iVal))
                                    prop.SetValue(model, iVal);
                                else if ((prop.PropertyType == typeof(double) || prop.PropertyType == typeof(double?)) && double.TryParse(str, out double dVal))
                                    prop.SetValue(model, dVal);
                                else if ((prop.PropertyType == typeof(decimal) || prop.PropertyType == typeof(decimal?)) && decimal.TryParse(str, out decimal mVal))
                                    prop.SetValue(model, mVal);
                                else if ((prop.PropertyType == typeof(float) || prop.PropertyType == typeof(float?)) && float.TryParse(str, out float fVal))
                                    prop.SetValue(model, fVal);
                                else if ((prop.PropertyType == typeof(bool) || prop.PropertyType == typeof(bool?)) && bool.TryParse(str, out bool blVal))
                                    prop.SetValue(model, blVal);
                                else if (prop.PropertyType.IsEnum)
                                {
                                    var enums = prop.PropertyType.GetFields().Where(p => p.FieldType.IsEnum);
                                    foreach (var item in enums)
                                    {
                                        var name = I18n.Get(item.GetCustomAttribute<DescriptionAttribute>()?.Description ?? item.Name);
                                        if (name == str)
                                            prop.SetValue(model, item.GetValue(null));
                                    }
                                }
                                else
                                {
                                    model.ImportStatus = enumExcelImportStatus.DataError;
                                    break;
                                }
                            }
                            else if (cell.CellType == CellType.Blank)
                            {
                                if (prop.GetCustomAttribute<RequiredAttribute>() != null)
                                {
                                    //必填项没有值 这一行设置错误状态 不再加载
                                    model.ImportStatus = enumExcelImportStatus.DataError;
                                    break;
                                }

                                if (prop.PropertyType == typeof(string))
                                    prop.SetValue(model, "");
                                else if (prop.PropertyType == typeof(DateTime?) ||
                                    prop.PropertyType == typeof(int?) ||
                                    prop.PropertyType == typeof(double?) ||
                                    prop.PropertyType == typeof(decimal?) ||
                                    prop.PropertyType == typeof(float?) ||
                                    prop.PropertyType == typeof(bool?))
                                    prop.SetValue(model, null);
                                else
                                {
                                    model.ImportStatus = enumExcelImportStatus.DataError;
                                    break;
                                }
                            }
                            else
                            {
                                model.ImportStatus = enumExcelImportStatus.DataError;
                                break;
                            }
                        }
                    }
                    catch
                    {
                        model.ImportStatus = enumExcelImportStatus.DataError;
                    }
                    if (model.ImportStatus == enumExcelImportStatus.Ready)
                    {
                        if (list.Count(p => p.Equals(model)) > 1)
                            model.ImportStatus = enumExcelImportStatus.Repeat;
                    }
                }
            }

            return list;
        }

        /// <summary>
        /// 获取导入模板的名称
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static string GetImportModelName<T>()
            where T : ImportModelBase, new()
        {
            var type = typeof(T);
            var tDes = type.GetCustomAttribute<DescriptionAttribute>();
            return I18n.Get(tDes == null ? type.Name : tDes.Description);
        }

        /// <summary>
        /// 下载导入模板
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="models"></param>
        /// <param name="title"></param>
        /// <param name="path"></param>
        public static void DownloadImportModel<T>(List<T> models, string title, string path)
            where T : ImportModelBase, new()
        {
            var type = typeof(T);
            var importProps = type.GetProperties().Where(p => p.Name != "ImportStatus").Select(p => p.Name);
            ExportHelper.Export<T>(path, title, importProps, models);
        }

        /// <summary>
        /// 自动生成示例数据，并下载导入模板
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="title"></param>
        /// <param name="path"></param>
        public static void DownloadImportModel<T>(string title, string path)
            where T : ImportModelBase, new()
        {
            List<T> models = new List<T>();
            var type = typeof(T);
            var maxCount = 1;
            var props = type.GetProperties().Where(p => p.Name != "ImportStatus" && p.Name != "Id").ToArray();
            foreach (var prop in props)
            {
                if (prop.PropertyType.IsEnum)
                    maxCount = prop.PropertyType.GetFields().Where(p => p.FieldType.IsEnum).Count();
            }
            for (int i = 1; i <= maxCount; i++)
            {
                var item = new T();
                foreach (var prop in props)
                {
                    if (prop.PropertyType.IsEnum)
                    {
                        var enums = prop.PropertyType.GetFields().Where(p => p.FieldType.IsEnum).ToArray();

                        if (i > enums.Length - 1)
                            prop.SetValue(item, enums.FirstOrDefault().GetValue(null));
                        else
                            prop.SetValue(item, enums[i].GetValue(null));
                    }
                    else if (prop.PropertyType == typeof(DateTime) || prop.PropertyType == typeof(DateTime?))
                    {
                        prop.SetValue(item, DateTime.Now.Date);
                    }
                    else if (prop.PropertyType == typeof(string))
                    {
                        var strVal = $"{(prop.GetCustomAttribute<DescriptionAttribute>()?.Description ?? prop.Name)}";
                        prop.SetValue(item, I18n.Get(strVal) + i);
                    }
                }
                models.Add(item);
            }


            var importProps = type.GetProperties().Where(p => p.Name != "ImportStatus").Select(p => p.Name);
            ExportHelper.Export<T>(path, title, importProps, models);
        }

    }
}
